Skip to content

fix(pm): repair two carriers still spelling the superseded references-tier boundary - #19379

Merged
hotlong merged 3 commits into
mainfrom
claude/pm-superseded-references-tier
Sep 22, 2026
Merged

hotlong merged 3 commits into
mainfrom
claude/pm-superseded-references-tier

Conversation

@os-steve

@os-steve os-steve commented Sep 20, 2026 •

Copy link
Copy Markdown
Collaborator

Part of #19146

Clause-②: no

Two carriers of the governed-tier rule still stated the 2026-09-13 boundary (Tier S / "fact layer" = only .claude/skills/pm-dispatch/references/**) that #19133 (2026-09-18) superseded. #19133's ruling, verbatim and untranslated: maintainer 「同意改规则。」 on the skills seat's proposal, plus the amendment that folded .claude/settings.json and .claude/hooks/** in too: 「我觉得这些我也没必要确认」. Tier S is now the whole .claude/** tree.

Current, correct source of truth (unchanged by this PR):

  • scripts/pm/check-governed-merges.mjs register row { id: 'claude-tree', prefix: '.claude/', glob: '.claude/**', tier: GOVERNED_TIER_S, … }, pinned by self-test case skills-agents-and-the-fact-layer-are-Tier-S.
  • .claude/skills/pm-dispatch/SKILL.md:625-626: 「受管面两层:Tier H(规则层)= AGENTS.md+CLAUDE.md+docs/adr/**+docs/NORTH-STAR.md+发布 skills/**。」「Tier S = .claude/** 全树;Tier H 四件套等人批;Tier S 经席内达档复核 PASS 在案后 ready → 入队。」

What was stale

.claude/skills/pm-dispatch/references/landing-operations.md:27-28

Before:

- 受管路径全在本技能 `references/` 者事实层:席内达档复核过落地前检三条即转正式入队。
- 其余为规则层:四件套留 draft 等人批,⛔ 不翻正式不入队;获授权批准后认领席落地。

After:

- Tier S(`.claude/**` 全树)者:席内达档复核过落地前检三条即转正式入队。
- Tier H(其余受管面)者:四件套留 draft 等人批,⛔ 不翻正式不入队;获授权批准后认领席落地。

Line count and byte ceiling unchanged (69/69, both lines within the 120-byte cap — check:pm-skill-ratchet verified).

scripts/pm/check-governed-queue-guard.mjs, the "THIRD leg" header (~268-294)

It reproduced the 2026-09-13 boundary and concluded: "Every other governed path is the rules layer and keeps the predicate above byte-for-byte" — false since #19133, and self-contradicting the same file's own later "the landing TIER" section, which already records that REFERENCES_TIER_PREFIX "is gone".

Fix: the quoted 2026-09-13 ruling is kept, untranslated, as the ruling that STARTED this leg (history is load-bearing — a reader who finds that text must see why it no longer governs). A new paragraph marks it SUPERSEDED by #19133 (cited with date, reusing this same file's own existing verbatim quote of the amendment for consistency) and points at governedTierFor / the register / node scripts/pm/check-governed-merges.mjs --test <paths> instead of a prefix to remember. The concluding sentence now reads "Every governed path outside Tier S is Tier H, the rules layer, and keeps the predicate above byte-for-byte."

No behavior changed: GOVERNED_SURFACES, governedTierFor, landingTierOf, every tier constant and every self-test assertion's expected value are untouched — only the two stale prose passages.

Verification before editing

Tier verdict on this PR's final file list

node scripts/pm/check-governed-merges.mjs --test .claude/skills/pm-dispatch/references/landing-operations.md scripts/pm/check-governed-queue-guard.mjs

→ GOVERNED — Tier S, exit 3. landing-operations.md hits the .claude/** register row; check-governed-queue-guard.mjs is not itself a registered surface (1 of 2 paths governed). Per Prime Directive #14, this PR lands on the owning seat's Tier S contract-tier review of record — no seat approves it, and no maintainer click is waited for.

Gates run (exit code captured before any pipe)

  • node scripts/pm/check-governed-queue-guard.mjs --self-test — exit 0 (296 cases pass)
  • node scripts/pm/check-governed-merges.mjs --self-test — exit 0 (435 assertions)
  • pnpm check:pm-skill-ratchet — exit 0 (landing-operations.md 69/69, headroom 0, unchanged)
  • pnpm check:pm-skill-id-lint — exit 0 (27 files clean)
  • pnpm check:pm-governed-prose — exit 0 (names all 6 registered surfaces)
  • pnpm check:nul-bytes — exit 0
  • npx eslint scripts/pm/check-governed-queue-guard.mjs — exit 0
  • node --check scripts/pm/check-governed-queue-guard.mjs — exit 0

Changeset

skip-changeset — no packages/* touched; neither .claude/skills/** nor scripts/pm/** ships in any package's files[] (same as precedent PRs #19144 and #19021).

On the card

This PR is filed as Part of #19146, not a new card: #19146 ("skills: re-key the three 事实层 = references/ spellings the Tier S ruling leaves false") was already open, filed by the seat that landed PR #19144, and its item 2 is exactly landing-operations.md:27-28. Creating a new duplicate card would have contradicted this repo's own duplicate-avoidance practice, so none was created. #19146's other items — .claude/agents/os-dev.md:286-287, check-half-states.mjs H48 and check-half-states.mjs H43 — are not touched by this PR and remain open on that card; neither is SKILL.md:608's own 事实层 wording (added to #19146 by its own addendum comment). H43 is the newest of them: it had lived only in card comment 5750573385 and is enumerated on the card body as item 4 by this rework. It is left here deliberately — H43 is missing LOGIC in a non-governed instrument (a LAZY governedTierFor load, because that row travels to sibling repos, plus one pnpm check:pm-half-states self-test case), which is the same change class as H48 and rides with it in ONE half-states PR rather than under a docs-only Tier S record. The at-tier review of record (5751616940) ruled this PR NOT incomplete for leaving it there. This PR additionally fixes scripts/pm/check-governed-queue-guard.mjs's self-contradiction, which is not named in #19146 at all.

Note on the dispatching brief

The brief that generated this PR stated "this repair has no card yet." That is not accurate: #19146 already existed (filed 2026-09-18, still open) covering part of this exact repair. Everything else in the brief — the ruling text, the register row, the self-test name, SKILL.md:625-626, and both stale passages — verified exactly as stated on direct reading.

Rework after the at-tier contract review (record 5751616940 — FAIL)

Head fa628d0b36 → 71216fcff6, one commit on the same branch (⛔ no rebase, no amend, no force-push — the review record is anchored to this branch's history). Both defects are TEXT: ⛔ no tier constant, no GOVERNED_SURFACES row, no governedTierFor, no landingTierOf and no self-test expected value moved. Self-test case counts are unchanged at 296 / 435.

1. landing-operations.md:27 — the PR-level ALL quantifier is restored.

The line shipped as 「- Tier S(.claude/** 全树)者:…」, which names the SURFACE. Its own predecessor (「受管路径全在本技能 references/ 者事实层」), this repair's prescribed wording on the card, and the sibling contract-review.md:46 (「受管路径全在 Tier S 面(.claude/**)者」) all carry the quantifier. Without it, lines 27 and 28 partition governed SURFACES rather than pull requests — so a mixed diff (a .claude/** path plus AGENTS.md, Tier H by the register's ALL-not-ANY rule) matched both lines with no tiebreak on the page.

-- Tier S(`.claude/**` 全树)者:席内达档复核过落地前检三条即转正式入队。
+- 受管路径全在 `.claude/**` 者 Tier S:席内达档复核过落地前检三条即转正式入队。

Re-measured here, not taken on trust: 93 B → 105 B against the 120 B cap, file 69/69 lines with headroom 0 (check:pm-skill-ratchet exit 0 names the file at 69/ceiling 69).

2. check-governed-queue-guard.mjs:4442-4443 — the --self-test SUCCESS line is re-keyed.

The docblock repair in the first commit left the one instance seats actually read: the SUCCESS line printed on EVERY run (it is in the review's own capture) still stated the superseded #18020 population, while the battery at :3949-3950 asserts ⛔ the-old-references-boundary-is-GONE. Landed 2026-09-13 in #18036 and untouched by #19144 — present at merge-base and at the reviewed head, reproduced here before the edit.

-      'the boundary a label reader cannot cross — and the #18020 references TIER: a governed diff whose governed ' +
-      'paths all lie under the one ruled prefix lands on the skills seat\'s review of record instead of an ' +
+      'the boundary a label reader cannot cross — and the #18020 references TIER, re-keyed to Tier S by #19133: a ' +
+      'governed diff whose governed paths are ALL Tier S — the register\'s `.claude/**` row, asked through ' +
+      '`governedTierFor`, never a prefix repeated here — lands on the skills seat\'s review of record instead of an ' +

History stays (the #18020 naming), exactly as the docblock keeps its quoted ruling; only the POPULATION is re-keyed. Proof it is gone from the PRINTED output, not merely from the source: --self-test at the new head prints the one ruled prefix 0 times and the re-keyed sentence once.

Still stating the superseded boundary — reported, ⛔ deliberately not pulled in

The review lists these as live and OUT of this PR's scope, and this rework leaves them exactly as it found them: .claude/agents/os-dev.md:286-287 (this card's item 1) and SKILL.md:608's 「⛔ 无事实层例外」 (a card addendum, vocabulary only — the rule itself stays true). Naming-only uses of "the references tier" as this leg's NAME (queue-guard :268, :358, :442, :452, :646, :736, code comments :1420 / :1546 / :2261 / :2470 / :2560, and check-clause2-carriers.mjs:8635) are an optional tidy and were left alone: widening the diff of a docs-only record to sweep names is not what the FAIL asked for.

Tier verdict on the FINAL file list

node scripts/pm/check-governed-merges.mjs --test .claude/skills/pm-dispatch/references/landing-operations.md scripts/pm/check-governed-queue-guard.mjs

→ ⛔ GOVERNED — Tier S(席内达档复核落地), exit 3; 1 of 2 paths on the register (.claude/** ×1 — landing-operations.md; scripts/pm/check-governed-queue-guard.mjs is not a registered surface). File list unchanged from the reviewed head, so the tier is unchanged. Per Prime Directive #14 this lands on the at-tier review of record — ⛔ no seat approves it and no maintainer click is owed.

Gates at the new head (exit code captured BEFORE any pipe)

The brief's minimum, plus every family node scripts/pm/dispatch-gates.mjs --commands derives for this change set — 38 commands, 37 at exit 0:

  • check-governed-queue-guard.mjs --self-test — exit 0, 296 cases (unchanged)
  • check-governed-merges.mjs --self-test — exit 0, 435 assertions (unchanged)
  • check:pm-skill-ratchet exit 0 (69/69, headroom 0) · check:pm-skill-id-lint exit 0 (27 clean) · check:pm-governed-prose exit 0 (6/6 surfaces, 28 self-test cases) · check:skill-frame-sync exit 0 · check:nul-bytes exit 0 (9053 files, no raw control bytes)
  • node --check exit 0 · npx eslint scripts/pm/check-governed-queue-guard.mjs exit 0 (1 file linted, 0 errors, 0 warnings, read from --format json)
  • check:pm-dispatch-gates, check:pm-governed-merges, check:ratchet-remedy-authority, check:doc-authoring, check:cross-package-test-inputs, check-declaration-mirrors, check-scripts-symbol-anchors, check-self-test-wired, check-self-test-workflow-commands, check-comment-mask-corpus and the rest of the derived list — all exit 0
  • ⊘ NOT MEASURED — pnpm --filter @objectstack/lint run check:doc-formula-expressions exit 3, PREREQUISITE NOT MET (@objectstack/formula and @objectstack/lint unbuilt in this worktree). Exit 3 is this repo's NOT-MEASURED code, ⛔ not a finding; the family's population is docs formula expressions, disjoint from this diff's two paths, and CI runs it against a built tree.

⚠️ dispatch-gates.mjs prints a STALE TREE warning: this branch is ≥55 commits behind origin/main and 15 files the derivation reads changed across that range. The gate list above is therefore derived from this branch's tree, which is what the review record is anchored to; ⛔ it was not refreshed by a rebase. CI on the merge group derives from the merged tree.

⚠️ check-clause2-carriers.mjs --pair 19379 reads 2 (UNJUDGED) — measured, and it is the BRANCH NAME

Reproduced at the new head: PM_SWEEP_REPO=objectstack-ai/objectstack node scripts/pm/check-clause2-carriers.mjs --pair 19379 → exit 2, 「the card's NEWEST claim comment (5754245926) matches the claim marker but its Branch: directive parses to ZERO branches」. The stored line 2 really is Branch: `claude/pm-superseded-references-tier` on a line of its own, so the printed remedy — "name the branch on a Branch: line of its OWN" — is already satisfied and cannot clear it.

The cause is not the regex named in the dispatching brief. BRANCH_TOKEN (check-clause2-carriers.mjs:3278) reads the Implemented-by: VALUE of a review record; it never sees a claim's Branch: directive. That directive is read by the sibling check-half-states.mjs:5407 claimedBranches, through CLAIM_BRANCH_SHAPE (:5358):

/claude\/issue-\d+-[A-Za-z0-9][A-Za-z0-9._-]*/g

which REQUIRES a literal issue- plus digits segment. Measured on the real stored comment body and two controls:

input claimedBranches()
the live comment 5754245926, as stored []
the same comment, branch swapped to claude/issue-19146-superseded-references-tier ["claude/issue-19146-superseded-references-tier"]
Branch: claude/issue-abc-slug`` (no digits) []

Only the branch NAME differs across those rows, so the marker, the backticks, the line position and the directive shape are all fine. claimGovernance on that one-comment thread returns governing: null with malformed: { id: 5754245926 } — which cardDeclaration turns into claim-branch-unparsed, i.e. exit 2.

The narrow shape is DELIBERATE where it was written (CLAIM_BRANCH_SHAPE's own docblock: a Branch: line naming some other shape "is deliberately left unmatched, which puts the card out of this row's scope entirely" — under-reporting beats manufacturing findings out of typos). The consequence in THIS reader is not out-of-scope, though: Prime Directive #14 makes --pair at 0 part of the Tier S landing predicate, so a Tier S PR on a branch without an issue-digits segment cannot satisfy it by any act of the claiming seat short of renaming the branch — which would strand this review record. ⛔ Not repaired here: check-clause2-carriers.mjs is ⛔ out of this PR's scope and the branch is ⛔ not renamed. Reported for the seat.


🤖 Generated with Claude Code

https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2


Generated by Claude Code


Generated by Claude Code

…-tier boundary

Two carriers of the governed-tier rule still stated the 2026-09-13 boundary
(Tier S / "fact layer" = only .claude/skills/pm-dispatch/references/**) that
#19133 (2026-09-18, maintainer "同意改规则。" plus the amendment folding in
.claude/settings.json and .claude/hooks/**) superseded: Tier S is now the
whole .claude/** tree, per the GOVERNED_SURFACES register (id: claude-tree)
and its self-test case skills-agents-and-the-fact-layer-are-Tier-S in
scripts/pm/check-governed-merges.mjs, and per SKILL.md:625-626. Within the
last hour this stale text caused two agents to misclassify a compliant PR.

- .claude/skills/pm-dispatch/references/landing-operations.md:27-28 spelled
  the old boundary as current ("受管路径全在本技能 references/ 者事实层 …
  其余为规则层"), which would tell a dev touching e.g. SKILL.md itself to
  leave the PR in draft awaiting a maintainer approval it does not need.
  Re-keyed to Tier S (.claude/** whole tree) / Tier H (everything else),
  same two-bullet shape, line count and byte ceiling unchanged (69/69).

- scripts/pm/check-governed-queue-guard.mjs's "THIRD leg" header (~268-294)
  reproduced the same narrow 2026-09-13 boundary and concluded "Every other
  governed path is the rules layer and keeps the predicate above
  byte-for-byte" -- false since #19133, and self-contradicting the same
  file's own later "the landing TIER" section, which already records that
  REFERENCES_TIER_PREFIX "is gone". The quoted 2026-09-13 ruling is kept,
  untranslated, as the ruling that started this leg; a new paragraph marks
  it SUPERSEDED by #19133 and points at governedTierFor / the register /
  `check-governed-merges.mjs --test <paths>` instead of a prefix to
  remember. No behavior, constant, or self-test assertion changed.

Tier verdict on this PR's final file list (check-governed-merges.mjs --test):
GOVERNED -- Tier S (landing-operations.md hits the .claude/** register row;
check-governed-queue-guard.mjs is not itself a registered surface).

The card relation to the pre-existing tracking issue is stated once, in the
pull request body, per this repo's own commit-trailer contract.

Co-Authored-By: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: fa628d0b3602a4df059add483084b23b826c85f9
Card: #19146 · PR #19379 (one commit; base ada701220b, 2 files, +22 / −10) · isolated adversarial review, measured in a detached worktree at the head, every exit code captured before any pipe.

① Derived judgments

② Semver level

None. Docs-only rules repair: no packages/* path, and neither .claude/** nor scripts/pm/** ships in any released package's files[]. skip-changeset is the correct label for this file list, and Clause-②: no is the correct declaration (no contract surface touched) — which the missing claim comment (③) must carry.

③ Boundary flags

Implemented-by: claude/pm-superseded-references-tier
Reviewed-by: session_01AmH9bKvGoLjiY86Q4Z3og2

VERDICT: FAIL


Generated by Claude Code

…line

Rework of the two one-line defects the at-tier contract review returned FAIL
on. Both are text; no behaviour, constant, register row, tier function or
self-test expected value moves.

- .claude/skills/pm-dispatch/references/landing-operations.md:27 named the
  SURFACE ("Tier S(.claude/** 全树)者") and dropped the PR-level ALL
  quantifier that its own predecessor ("受管路径全在 … 者"), this repair's
  prescribed wording and the sibling contract-review.md:46 all carry. As
  written, :27 and :28 partitioned governed SURFACES rather than pull
  requests, so a mixed diff (.claude/** plus AGENTS.md, Tier H by the
  register's ALL-not-ANY rule) matched both lines with no tiebreak on the
  page. Restored to "受管路径全在 `.claude/**` 者 Tier S:…" — 93 B → 105 B
  against the 120 B cap, line count 69/69 unchanged.

- scripts/pm/check-governed-queue-guard.mjs:4442-4443, the --self-test
  SUCCESS line printed on every run, still stated the superseded #18020
  references-tier population ("a governed diff whose governed paths all lie
  under the one ruled prefix") while the battery at :3949-3950 asserts
  the-old-references-boundary-is-GONE. Landed 2026-09-13 in #18036 and
  untouched since, so the file's docblock repair left the one instance seats
  actually read. The population is re-keyed to Tier S — the register's
  .claude/** row, asked through governedTierFor — with the #18020 naming
  kept as history, matching the docblock's own form. 296 cases unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 71216fcff6d2cd314784ce7746ccd91095751ea7
Card: #19146 · PR #19379 · second at-tier review, after record 5751616940 (FAIL on fa628d0b36). Measured in a detached worktree at the sha above, fetched from origin first: the shared checkout's LOCAL ref claude/pm-superseded-references-tier still reads fa628d0b36 and was left untouched; origin, and the PR's head, read 71216fcff6 — two commits on base ada701220b, no rebase. Every exit code captured before any pipe.

① Derived judgments

  • Tier, re-derived on the final file list: check-governed-merges.mjs --test landing-operations.md check-governed-queue-guard.mjs → GOVERNED — Tier S(席内达档复核落地), exit 3, 1 of 2 paths on the register (.claude/** ×1); the .mjs is not a registered surface. Tier claim in the PR body: CORRECT. Register probed on real paths, one each: .claude/settings.json, .claude/hooks/guard-main-checkout.sh, .claude/agents/os-dev.md, .claude/skills/pm-dispatch/SKILL.md, .claude/README.md → Tier S; skills/x/SKILL.md, AGENTS.md, CLAUDE.md, docs/adr/0001-x.md, docs/NORTH-STAR.md → Tier H (the #9495 regime rendering, landing tier: H(人合)); .claude/agents/os-dev.md + AGENTS.md → Tier H; settings + hooks + agents → Tier S; scripts/pm/*.mjs, .claudeX/x.md, docs/adrs/x.md → not governed, exit 0. The register has six rows; .claude/** (claude-tree) is its only GOVERNED_TIER_S row and the field is tier.
  • landing-operations.md:27 — REPAIRED, CORRECT. Head reads 「受管路径全在 .claude/** 者 Tier S:席内达档复核过落地前检三条即转正式入队。」. The quantifier is over GOVERNED paths (受管路径全在), which is exactly governedTierFor's ALL-over-governed-paths semantics, so an unregistered sibling such as this PR's own scripts/pm/ file does not break it. It matches the card's prescribed head clause, the merge-base form (「受管路径全在本技能 … 者」) and contract-review.md:46 (「受管路径全在 Tier S 面(.claude/**)者」). Partition probed: pure .claude/** diff → :27 only; .claude/** + AGENTS.md → :28 only (「Tier H(其余受管面)者」); pure Tier H → :28 only — exactly one line applies in each case. Bytes re-taken: :27 105 B (93 B at fa628d0b36, 117 B at merge-base), :28 120 B — at the cap, so the next edit of :28 must shrink it; file 69 lines, no line over 120 B; check-skill-line-ratchet exit 0 (69 / ceiling 69, headroom 0). The 5000-line carve-out lives on :26, not :27; the block reads top-down, no action.
  • Queue-guard docblock (:286-:304): CORRECT against the register — sole S row, the other five rows H; governedTierFor imported at :513, driving every entry's tier at :972 and the replay at :2804, re-exported at :728. The amendment quote is reused, not re-typed: the file's earlier instance at :668-:670 is line-wrapped (「我觉得这些我也没 / 必要确认」), so a one-line grep reads 1 and the wrapped control reads 2.
  • --self-test SUCCESS line (:4442-:4445) — population: REPAIRED. Printed output at head carries 「the one ruled prefix」 0× (merge-base 1×) and the re-keyed sentence 1×; it now agrees with the battery at :3949-:3953 (a-Tier-S-only-path-set-is-Tier-S, the-old-references-boundary-is-GONE…, ONE-Tier-H-path-makes-the-WHOLE-entry-Tier-H).
  • --self-test SUCCESS line — actor: DEFECT, live. The same sentence still reads 「… a governed diff whose governed paths are ALL Tier S … lands on the skills seat's review of record instead of an authorized approval」. That actor is the pre-skills(governed): narrow the human-merge floor to the law — Tier H stays human/approved (AGENTS.md · CLAUDE.md · docs/adr/** · docs/NORTH-STAR.md · .claude/settings.json · .claude/hooks/**); Tier S (.claude/skills/** · .claude/agents/**) lands on the seat's CONTRACT_REVIEW_TIER PASS + post-merge audit #19133 one, from when the ruled prefix was the skills lane's own. The regime the sentence says it is re-keyed to names a different actor everywhere else: AGENTS.md Prime Directive feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14 「Tier S by the owning seat on a contract-tier review of record」 and 「the owning seat then lands it through the queue」; the register check-governed-merges.mjs:316 / :1015 / :1559 「the owning seat lands it」; this file's own printed verdicts :1426 「each carries the owning seat's」 and :1489 「the owning seat posts its review of record」; SKILL.md:522 / :609 and contract-review.md:26 put the ② review in the OWNING seat when it is at tier (the skills seat is only the hand-off for a seat outside the tier). The only other 「skills seat」 in the file (:286, :668, 「on the skills seat's proposal」) is history and correct. Counter-example on this very record: PR fix(pm): repair two carriers still spelling the superseded references-tier boundary #19379 is Tier S, owned by domain:spec seat 4, its claim (5754245926) and both at-tier records rendered on that seat — not the skills seat's. On the most-read sentence in the file, present tense, inside the clause that announces the re-key, a wrong actor is a live mis-statement of who lands Tier S — the same class as the population defect the prior record failed on. Required: one word, 「skills seat's」 → 「owning seat's」 in the :4444 literal. Measured to move nothing: no pin holds that text (0 hits outside the file; inside it the phrase is split across two literals by construction), no expected value, 296 cases unchanged.
  • Behaviour, proven by test and by AST: the comment-stripped md5 of the .mjs is identical at merge-base and fa628d0b36 (0 differing AST lines); merge-base vs head differs in 51 AST lines, ALL inside the selfTest() success-line string chain (node count 20741 → 20744: one BinaryExpression, one PlusToken, one StringLiteral); no other node moved. So 「no behaviour changed」 is true up to the printed sentence, and the PR body's exact wording (no constant, row, tier function or expected value moved) holds. Self-tests at head AND at merge-base: queue-guard 296 / 296, exit 0; governed-merges 435 / 435, exit 0 — NOT MEASURED (exit 3, PREREQUISITE NOT MET) until pnpm install --frozen-lockfile --ignore-scripts in my worktrees, then measured on both trees; half-states 5092, exit 0; clause2 1071, exit 0.
  • Gates at head: skill-line-ratchet 0, skill-id-lint 0 (27 clean), governed-prose 0 (2 surfaces name all 6), skill-frame-sync 0, node --check 0, widening-tells --declaration no on the three-dot diff 0 (no tell; no declared surface covers either path). CI on the head: 38 check-runs, 27 success / 11 skipped / 0 failure; required contexts Lint & Repo Gates, TypeScript Type Check, Test Core, Dogfood Regression Gate and Governed Surface Queue Guard success; Build Core and Temporal Conformance skipped on the docs-only paths filter.

② Semver level

None. No packages/* path; 76 package manifests scanned, 0 files[] naming .claude or scripts/pm. skip-changeset is correct for this file list, and Clause-②: no is declared on both carriers — the pair record reads no from claim 5754245926 and no from the PR body — with no widening tell on the diff.

③ Boundary flags

  • RULING on --pair 19379 = exit 2 (UNJUDGED): this PR is UNLANDABLE as it stands, on this branch, whatever this verdict says. Re-measured at head: PM_SWEEP_REPO=objectstack-ai/objectstack node scripts/pm/check-clause2-carriers.mjs --pair 19379 → exit 2, pair.1.head-sha equal to the sha above, pair.1.claim.selected: NONE — the newest claim (5754245926) matches the marker but its Branch: line parses to zero branches (claim-branch-unparsed, cardDeclaration :2398 / :2416). Mechanism reproduced first-hand through claimedBranches (check-half-states.mjs:5407) on the stored body: the live claim → 0; the same body with the branch swapped to claude/issue-19146-superseded-references-tier → 1; claude/issue-abc-slug → 0; claude/issue-19146-x → 1. Only the branch NAME differs, so the printed remedy is already satisfied and cannot clear it. Prime Directive feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14 makes --pair at 0 a conjunct of the Tier S landing predicate and ends 「No seat judges this」: exit 2 is not a deviation a seat may declare and land over (the rework report's option A), and a PASS record here would still not make the PR landable. Where the fault lies: the branch, not the checker. AGENTS.md:460 「Name the branch after the issue it fixes」 with the shape claude/issue-NNNN-slug, and :395 (the claim names the branch in that shape), are the contract; CLAIM_BRANCH_SHAPE (:5358) is that rule made mechanical, and check-closing-target-claim.mjs imports the same reader. The class-(b) finding the rework report files against the checker is therefore mis-aimed — the predicate is unsatisfiable only for a branch that already violates the naming rule — and widening the shape (option B) would be a workaround of a declared rule (Prime Directive [WIP] Fix error in step four of the action run #5). The remedy is on the branch side and is the owning seat's act, ⛔ not performed here: a conforming name carrying these same two commits (a GitHub branch rename keeps the PR, its thread, its head and both records — nothing is stranded, the records stay as history), then one new Claim: naming it and one fresh at-tier record whose Implemented-by: names it, at whatever head the FAIL above is repaired on. Two follow-ons for the skills seat, not this PR: contract-review.md:42 enumerates the ② readings as 0 / 4 / 3 only — exit 2 is not in the skill's vocabulary; and the green check-run 「The card this PR closes must claim this branch」 on this head is vacuous (Part of #N is not a closing keyword, check-closing-target-claim.mjs:82 / :584) and vouches for nothing about this claim.
  • Residual superseded-boundary statements after this head, swept by meaning over tracked files. LIVE: .claude/agents/os-dev.md:286-:287 (card item 1 — the file every dev subagent reads). VOCABULARY only, rule still true: SKILL.md:608 「⛔ 无事实层例外」 and check-skill-line-ratchet.mjs:327 「no fact-layer exception」. NAMING only: 「references tier」 as the leg's name at queue-guard :268, :442, :452, :646, :736, :1420, :1546, :2261, :2470, :2560, :3929 and check-clause2-carriers.mjs:8635. HISTORY, correct as written: queue-guard :275 (the quoted ruling), :674-:675 (「it is gone」), :3937; check-governed-merges.mjs:321; check-clause2-carriers.mjs:8647. The scope line is drawn acceptably — the diff repairs what it names and adds an off-card file — with one obligation it creates: the skills seat's addendum (5737973707) prescribed the three spellings 「one PR, ceilings unchanged」, and this PR delivers one of the three, so os-dev.md:286-287 and SKILL.md:608 are now owed as a follow-up on skills: re-key the three 事实层 = references/ spellings the Tier S ruling leaves false (os-dev.md :286–:287, landing-operations.md :27–:28, H48) #19146; the PR body says so, the card body should.
  • H43 / H48: not re-litigated; the prior ruling stands, and the card body now enumerates H43 as item 4 (verified on the live body).
  • Commit form, REPORTING only: both branch commits end with the harness Co-Authored-By trailer and neither carries the Claude-Session: line AGENTS.md:440-442 pairs it with; check-commit-card-trailers.mjs refuses a model identifier or a card relation in the pair and does not require the session line, so the pre-push hook passed. Landed history is not rewritten (⛔ no amend, no force-push). No model identifier in either commit or in the PR body.
  • Identity: Implemented-by is the mode:subagent dev's branch and Reviewed-by the adopting seat's session (contract-review.md:35-:37); an isolated subagent under scratchpad/pr-19379-r2/ produced this record from the card, the rulings, the prior record and the PR; the pair reads branch vs session, not SELF-REVIEW.
  • Nothing in this review modified the PR, its branch, body, labels or state; no push, no approval, no enqueue; the sibling worktrees named in the brief were not entered.

Implemented-by: claude/pm-superseded-references-tier
Reviewed-by: session_01AmH9bKvGoLjiY86Q4Z3og2

VERDICT: FAIL


Generated by Claude Code

The re-keyed Tier S sentence printed on every --self-test run still named the
skills seat as the actor a Tier S landing rests on. That is the pre-re-key
actor: the Prime Directive, the surface register, this file's own printed
verdicts and the dispatch skill all say the owning seat. One word inside the
literal; no constant, register row, tier function or expected value moves, and
the case count is unchanged.

Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2
Co-Authored-By: Claude <noreply@anthropic.com>
@hotlong
hotlong marked this pull request as ready for review September 22, 2026 07:22
@hotlong
hotlong enabled auto-merge September 22, 2026 07:22
@hotlong
hotlong added this pull request to the merge queue Sep 22, 2026
Merged via the queue into main with commit 1f53b0b Sep 22, 2026
33 checks passed
@hotlong
hotlong deleted the claude/pm-superseded-references-tier branch September 22, 2026 07:44
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…es option, and a pin reads the ruleset (objectstack-ai#19448)

Fixes objectstack-ai#19344

Clause-②: no

Item 6 of the maintainer's ratified directive — verbatim 「1 2 3 4 5 6 8」
(record `5750077963`, relayed onto this card as comment `5750078192`) —
narrowed by the director's pointer `5749924838`: route **(a)** was taken
in person, PR objectstack-ai#19024 merged by bypass (`merged_by` os-zhuang, squash
`2277d1f`). What remained is exactly what this PR does — the guard's
remedy sentences name a path that EXISTS on ruleset `main`, and a pin
fails when they name one it does not offer.

## The defect

Ruleset `main` (id 12119582, enforcement `active`, target the default
branch) carries a `merge_queue` rule and lists `Governed Surface Queue
Guard` among its seven required contexts. So the only Merge button an
ordinary account is offered is "Merge when ready" = enqueue, and the
size limb then refuses the queued group. The remedy prescribed 人工直合 —
"the maintainer's own click" — without naming WHICH click, and the only
click that is not an enqueue is the Merge button's **bypass-rules**
option, offered only while the ruleset configures a bypass actor. With
none configured, the remedy named a terminal nobody could reach: PR
objectstack-ai#19024 was enqueued three times and refused three times.

## Before / after — the four sentences

Each keeps 人工直合 as the NAME of the act (the 2026-09-18 ruling stands
untouched); what changes is the description of how the act is reached.

**1. The header sentence (`:410`, the size limb's own contract)**

Before:

```
 * maintainer's own click (人工直合). An authorized APPROVED review lifts a
```

After: the same opener, then — `main` mandates the queue and requires
this check, so the only Merge that is not an enqueue is the Merge
button's BYPASS-RULES option, offered only while the ruleset configures
a bypass actor; while none was, the remedy named a terminal nobody could
reach and PR objectstack-ai#19024 was enqueued and refused three times; that it IS
offered is a ruleset fact the pin reads.

**2. The governed limb's early warning (`renderGuardVerdict`, Tier H
block)**

Before:

```
Unapproved, the maintainer's own direct merge (人工直合) is
the only landing this pull request has.
```

After:

```
Unapproved, the maintainer's own direct merge (人工直合) is the
only landing this pull request has, and it IS the Merge button's bypass-rules option —
offered only while ruleset `main` configures a bypass actor (objectstack-ai#19344).
```

**3. The governed limb's refusal (`renderGuardVerdict`, remedy item 2)**
— same replacement, plus "the audit log records it".

**4. The SIZE limb's refusal (`renderSizeVerdict`, remedy item 2)** —
rendered on the objectstack-ai#19024 shape:

```
        2. Then a HUMAN MERGE — the same terminal a governed diff has: ACCEPT on the card,
           `needs-user-decision` on the PR, a final 维护者速读, review requested from GOVERNED_APPROVERS
           (os-zhuang, hotlong); the maintainer's own click lands it (人工直合) — and that
           click is the Merge button's bypass-rules option, offered only while ruleset `main` configures a
           bypass actor — ⛔ NOT a second Merge button: `main` mandates the queue and requires this check, so
           with none configured every re-enqueue comes back here (objectstack-ai#19344). The audit log records the bypass
           and `check-governed-merges` lists such a landing on size.
```

## The ruleset reading this seat measured

`GET /repos/objectstack-ai/objectstack/rulesets/12119582`, with this
seat's token, on the day this PR was written: **HTTP 200**, and the
response carries **no `bypass_actors` key at all** — not `null`, absent.
The keys it does return are `id name target source_type source
enforcement conditions rules node_id created_at updated_at
current_user_can_bypass _links`, and `current_user_can_bypass` reads
`"never"`.

That is a different fact and is recorded beside it: it says THIS token
is not itself a bypass actor, which is true of every agent seat and says
nothing about whether the ruleset configures one for the maintainer. The
director's earlier pointer read the field as `null`; this seat's read
gets no key. Both are "cannot conclude", and the pin treats them
differently only in what it prints.

`check-required-contexts.mjs`'s measured header is the authority on why:
the ruleset endpoints answer 200 for `metadata=read`, but the bypass
roster is an `administration` field, and `administration` is not one of
the 17 permissions a workflow may grant its `GITHUB_TOKEN`. So no token
this repository's CI can hold will ever read `bypass_actors`.

## The pin, and its three verdicts

One new battery in `--self-test`, five cases, driving two new pure
exports (`bypassActorReading`, `remedyPathVerdict`) over a frozen copy
of the measured response:

| `bypass_actors` as read | reading | pin |
|:--|:--|:--|
| key absent (this seat's and every Actions token's answer) |
`unreadable` | **PASSES**, printing `bypass_actors: unreadable with this
token (the key is absent); current_user_can_bypass: "never"` |
| present and `[]` or `null` | `not-offered` | **REDS** — the remedy
names a path the ruleset does not offer |
| present with at least one actor | `offered` | PASSES |

Two ways to red, not one: the ruleset is READ to offer none, **or** the
remedy stops naming a path at all — which is the defect this card filed,
and a pin that only checked the first would sit green through it. The
self-test prints the reading on every run, so "unreadable" is never a
silent pass. ⛔ It never asserts a path from a field it did not read, and
⛔ it never reds CI on a permission difference.

## Recorded, not live — the four axes

The card allowed either a live read or a recorded fixture. **Recorded,
and the live read deliberately not added.**

- **实际业务需求** — measured, not supposed: `bypass_actors` is unreadable to
this seat's token AND to any Actions token (above). A live read wired
into this self-test would therefore answer `unreadable` on every CI run
in existence — it would assert nothing, on every run, while adding a
network call. The real need is that the remedy sentence names a
reachable path; the only party who can verify reachability is the
maintainer, and their verification already happened (the objectstack-ai#19024 bypass
merge). A recorded reading is what that evidence looks like in this
file.
- **项目长远合理性** — this self-test is the **first step** of the required
`Governed Surface Queue Guard` job, run under `bash -e` as the
precondition for trusting the guard, and its own usage line declares it
`offline, no network, no git`. Making a merge precondition depend on
api.github.com reachability and on a token's permission tier is the
permanently-red-gate shape this repo has already retired. The precedent
is in-repo and exact: `check-required-contexts.mjs` keeps a frozen
`RULESET_SNAPSHOT` in its self-test and leaves the live diff to a
report-only mode that never runs in CI, for the same structural reason.
- **防 AI 写代码犯错** — a live read is the lenient-consumer shape here: it
passes for every token that cannot see the field, so the assertion would
be phantom and the next author would read green as "the path is
reachable". The recorded form makes the claim declared and falsifiable —
change the remedy, and the pin demands a reading that offers the path;
the reading is printed rather than swallowed.
- **创业阶段不扩散需求** — one battery, two pure exports, one frozen object, no
new entry point, no new script, no new workflow, no widened token scope.
Net **+60 lines**, exactly the budget.

The fixture's cost is drift, and it is bounded on purpose: it carries
its provenance in the comment above it (endpoint, id, read date, token
class), its reading is printed on every self-test run rather than
asserted silently, and the field it records is one no CI token can
re-read anyway — so a live read would not have detected drift either.

## Evidence

- `node scripts/pm/check-governed-queue-guard.mjs --self-test` :: exit 0
— **301 cases** (296 before; +5 is exactly the new battery), and the run
prints `ℹ objectstack-ai#19344 ruleset reading — bypass_actors: unreadable with this
token (the key is absent); current_user_can_bypass: "never"`.
- **Ablation** (`scripts/ablation-replace.mjs`, wrap mode, on the
committed state): anchor `click is the Merge button's bypass-rules
option` replaced in `renderSizeVerdict` — on-disk proof `anchor 1 -> 0,
blob 5b75964 -> e6ee4e00f482` — self-test went **red, 3 of 301**,
naming `all-four-remedies-NAME-the-bypass-rules-option…`,
`a-field-this-token-cannot-see-is-UNREADABLE-and-PASSES…` and
`one-configured-bypass-actor-makes-the-named-path-REACHABLE…`. Restore
proven byte-identical: `blob == HEAD (5b75964)` and `git diff HEAD`
empty. The direction is the expected one (red), and it reds via BOTH
limbs of the contract, which is what "two ways to red" means.
- **Derived gate union** (`node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack`, no paths, 31 families): 30 run with
exit captured before any pipe, **all exit 0**; `pnpm
check:pm-dispatch-gates` was still running when this PR was opened and
is reported in the card comment. `--ran` reconciliation: 31 derived, 30
accounted with real exit codes, 0 NOT-MEASURED among them.
- `node scripts/pm/check-governed-merges.mjs --test
scripts/pm/check-governed-queue-guard.mjs` :: exit 0 — **NOT governed**;
ordinary queue landing applies. `--pr 19024` :: exit 3 — the
size-decided landing is listed, which is the recognition the remedy text
now points at.
- PM mechanism assumptions: (1) confirmed — the three sentences read as
described on `origin/main`; (2) PR objectstack-ai#19379's region `:268–:294` is
untouched by this diff; (3) confirmed — the self-test is wired at
`.github/workflows/governed-surface-guard.yml`, extended in place, no
second entry point; (4) confirmed above.

## Acceptance notes

- The ruleset response carries `current_user_can_bypass`, which an
ordinary token CAN read and which directly answers "is the bypass option
offered to the account asking". Nothing in this repo probes it live; it
is recorded in this fixture only. Observation, not filed — it is a
capability nobody has pulled on, and the four-axis call above is not to
add a live probe to a required precondition.
- `check-required-contexts.mjs`'s `RULESET_SNAPSHOT` records the
2026-08-18 reading with **six** required contexts; the live ruleset now
carries **seven** (`Governed Surface Queue Guard` joined since). That
file's assertions are deliberately written on the SHAPE, not on
membership, and its header says so, so nothing is wrong — but the
snapshot is a frozen historical reading and reads at a glance like a
current one. Observation, not filed.
- ⛔ Not touched, per the card's own "Not this card": the 5000-line
threshold (it lives in `check-governed-merges.mjs`, imported here), the
required-context set, the 「THIRD leg」 header at `:268–:294` (PR objectstack-ai#19379's
region), and any workflow.

---
_Generated by [Claude
Code](https://claude.ai/code/session_017ETYWqMQD4qMtZzAGovWNi)_

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…tems 1 2 3 4 5 8 — as in-place charter deltas, net 0 per ratcheted file (objectstack-ai#19449)

Fixes objectstack-ai#19340
Clause-②: no

Lands the DELTA between the six adopted items of the director's record
(comment 5750077963 on objectstack-ai#19340 — the maintainer's verbatim answer 「1 2 3
4 5 6 8」, 2026-09-20T13:24Z, items 1 2 3 4 5 6 8 adopted, 7 and 9 not)
and the charter as it stood on `origin/main` `23f1de0`. Item 6 is
carried by objectstack-ai#19344 (out of scope here; objectstack-ai#19344 remains open). Items 7 and
9 were not adopted and nothing here re-proposes them.

Every ratcheted file lands at **net 0** — each new rule is paid by
retiring a second pointer to the same reference file or a line restated
verbatim in a sibling (the pointer-retirement pattern of the last two
director-charter PRs) — and every landed line is at or under 120 bytes
(widest: 120 B). `state-machine.md` is untouched (reasons below). Diff:
4 files, +24 / −24.

## Per item — the line that stood, the line that lands, what was already
in place

| item | before (`origin/main` `23f1de0`) | after (this branch,
`a2665ce`) | already in place — untouched |
|:--|:--|:--|:--|
| **1** filing threshold inverted | `SKILL.md` :347–:351 named the three
classes and 「其余进 PR `## Acceptance notes`,⛔ 不立卡」 but said nothing about
reachability today, a named producer, dormant / zero-pull findings,
digests or quotas | `SKILL.md` :351 (new) 「(a) 须今天可达,(c)
须具名生产者;观察、休眠、零拉动 ⛔ 不立卡、不进汇总卡、无配额」 · `os-dev.md` :45 「(a) 须今天可达,…」, :46
「…须具名生产者…」, :48 adds 休眠、零拉动 to the not-filed list · `core-rules.md` :87
twin (今日可达可复现缺陷 · 具名生产者陷阱) | `SKILL.md` :347–:350 (three classes,
(a)/(c) boundaries), :352–:354 (承接者:无), :376 (三类外关 not planned);
`os-dev.md` :41–:44, :47 |
| **2** devs do not file; report contract | `SKILL.md` :778 「只列三类立卡与
`noted, not filed`;立卡附查重词、归挂、立在修复仓」 and :779 「席位在 ACCEPT 读 … 由席位补立」
named no `class:` / `carrier:` field and no per-entry disposition;
`os-dev.md` :49–:50 spelled entries as `noted, not filed: …`; :60–:61
and :65 still read as the dev filing (「立成它的 sub-issue」「独立立单」「无 assignee
立单」) | `SKILL.md` :777 (every entry `class: a\|b\|c` + evidence, or
`carrier:` = 承接者; neither ⇒ Acceptance notes), :778 (dev 不立卡; ACCEPT
disposes each entry in one line `filed #N` / `Acceptance notes` /
`dropped — reason`; the seat files the three-class ones in the fix
repo), :779 (seat reads Acceptance notes, files the true three-class
ones with 归挂 + 查重词; out-of-class cards already filed are closed not
planned) · `os-dev.md` :49–:50 (the two fields; `carrier:` defined;
neither ⇒ Acceptance notes only), :60–:61, :65 (the REPORT names the
sub-issue / `Blocked-by:` anchor; the seat files), :376 (report
template) · `core-rules.md` :151 twin | `os-dev.md` :55 「dev 不 `POST
/issues`」 already said devs do not file; :51 (3–5 dedupe words); :53
(four-write budget) |
| **2** the mechanical lock | see **Item 2's lock — measured** below |
no file change | MCP half: `issue_write` and `sub_issue_write` are in
`settings.json` `permissions.deny`, pinned by
`check-settings-deny-roster.mjs` |
| **3** triage merges at first grading | `SKILL.md` :367 「同文件同缺陷 =
同一发现,不分车道:证据搬到先卡,后卡关 `duplicate_of`,⛔ 不并排派发」 and :368 「其余在飞 ⇒
`Blocked-by:` 不派;open 未认领 ⇒ 先并成一个派发入口;已完成 ⇒ 卡可能过期」 — a label-pass rule
with no first-touch timing, no same-family rule and no execution-seat
route | `SKILL.md` :368 「同文件同机制 = 同一发现,不分车道,首触定级即并:证据搬先卡,后卡关
`duplicate_of`」, :369 「同族异缺陷 ⛔ 不并:互链排同批;在飞(assignee/open
PR)永不并,`Blocked-by:` 不派;完工查过期」, :370 「执行席 ⛔ 不并卡,疑重复挂 `pm:retriage`
写明哪两张同文件同机制;…」 | `SKILL.md` :223 (分诊唯一生产 `duplicate_of`), :224 (执行席误标 ⇒
`pm:retriage`), :366 (查重先按文件/机制); `core-rules.md` :137 (去重并卡 = 记账不升级) —
no core-rules line states the merge rule itself, so no twin |
| **4(i)** escalated card names its half | nothing: `SKILL.md` :454–:455
(在飞卡衍生三分 / sub-issue) and :471 (`Release:` 行点名已落项与余项去向) never required
the new card's face to say which half it carries | `SKILL.md` :458 (new)
「自在飞派发升级出的卡首行一句:本卡承哪一半、父卡留哪一半;缺此行不入队不入箱」 | `SKILL.md` :456–:457
(sub-issue inherits domain / priority; the one dispatch bypass), :471 |
| **4(ii)** ruled card re-enters only with `Prior rulings on this card:`
| `SKILL.md` :357 「决策箱勤务:落卡入箱时校验/补全四棱块与速读;…」 checked the 速读 and
four-facet block only; no intake refusal existed anywhere | `SKILL.md`
:357 (intake also checks the `Prior rulings on this card:` line), :358
(new) 「线程有 `Ruling:`/RULED 的卡再入箱,该行逐 id 各一句变化;缺行 ⇒ 拒入,`pm:retriage`
回立卡者」 | `SKILL.md` :340 (`Prior rulings read:` line at classification),
:733 (出决策箱须引裁决 id); `decision-analysis.md` :45; `dispatch-runbook.md`
:84 (`Ruling-ref:`); `state-machine.md` :40–:41 (who hangs and who
removes `pm:retriage`) |
| **5** first batch after five cards | `director.md` :40
「第一步:逐卡过一类自裁三判据;全立者录裁转工作态、只入收班追认表,⛔ 不呈批」 — read as 「screen the whole
inbox, then present」 | `director.md` :40 「首批读完前 ≤5 张同族卡即呈;一类自裁按批过,⛔
不先扫全箱,自裁者由下一候选补位」, :41 「取下五张只用便宜扫描:标签、assignee、PR 引用、速读首行;全文只读手上五张」, :42
「判据:开场标记→首批 ≤ 读五张;全立者录裁转工作态、只入收班追认表,⛔ 不呈批」 (the old :40 tail kept
verbatim) | `director.md` :44 「一次只开一批」 (pacing, not the pre-read); :23
(每批 5 张) |
| **8** same-holder duplicates | `SKILL.md` :223
「分诊座位唯一生产:…`duplicate_of`」 with no exception anywhere | `SKILL.md` :370
tail 「同 assignee 者自关后卡」 — the holding seat closes the later card as
`duplicate_of` itself, no `pm:retriage` round trip | :223 kept verbatim:
no 120-byte spelling of the exception fits on it (four variants measured
125–153 B); the exception sits beside the merge rule it widens |

## Paid for by retiring five second pointers and two restated lines —
net 0 per file

| retired | what it was | the rule still lives at |
|:--|:--|:--|
| `SKILL.md` :321 「`since` 读法与成本对价见 `references/dispatch-runbook.md`」 |
pointer, one of six to the same file | `dispatch-runbook.md`, still
pointed at from :384, :532, :588, :729 |
| `SKILL.md` :560 「云卡四课与接手协议增量见 `references/dispatch-runbook.md`」 |
pointer | `dispatch-runbook.md` :115 「## 云卡(`mode:cloud`)四课」, :143 「##
接手中断的 dev」; pointed at from :532 |
| `SKILL.md` :650 「os-regen 的陷阱与锚点禁令见 landing-operations A」 | pointer,
second to the same file | :637 「细则见 `references/landing-operations.md`」;
`os-dev.md` :197–:198 carry the trap |
| `SKILL.md` :657 「首次入队 flip 定点、MERGED 两读数、关键 PR 订阅与退订/归档细则见
landing-operations B」 | pointer, third to the same file | :637 |
| `SKILL.md` :771 「总监席不占 `domain:*`,永不认领 backlog、永不写码;章程见
`references/lanes/director.md`」 | restated verbatim + duplicate pointer
| `director.md` :19 (the rule); `SKILL.md` :57 and :675 (the pointer) |
| `director.md` :42 「一行回批即全链执行,裁后四件原子执行不再请示」 | restated |
`decision-analysis.md` :14 (⛔ 不为它另起请示轮), `SKILL.md` :728 (四件同笔);
`director.md` :46 points at decision-analysis |
| `director.md` :51 「代裁通道的置信门与人工地板见 SKILL.md 分诊座位职责,⛔ 不另抄」 | pointer |
`SKILL.md` :392–:399 (人工地板, 置信门), :770 (总监席是唯一裁决者) |

Clauses compressed inside a rewritten line, and where each now lives:

| clause | now covered by |
|:--|:--|
| `SKILL.md` :357 「语言按不变量」「子轮」 | :99 (中文只留四通道), :372 (低频子轮) |
| `SKILL.md` :367 「⛔ 不并排派发」 | :368 merge at first grading + :369 「在飞 …
`Blocked-by:` 不派」 + :370 (execution seat raises `pm:retriage`): two
same-finding cards can no longer be dispatched side by side |
| `SKILL.md` :368 「open 未认领 ⇒ 先并成一个派发入口」 | superseded by 「首触定级即并」 (:368)
and 「同族异缺陷 … 互链排同批」 (:369) — item 3 moves the merge from the dispatch
entry to first grading |
| `SKILL.md` :368 「已完成 ⇒ 卡可能过期」 | kept as 「完工查过期」 (:369) |
| `os-dev.md` :46 「由写它的人以外的人」→「他人」 · :48 「文档 nit」→「nit」 | same meaning,
21 B and 7 B shorter |
| `os-dev.md` :49 「席位 ACCEPT 读」 | `SKILL.md` :778–:779 (the seat's
ACCEPT disposition) |
| `core-rules.md` :87 「先发修复指令再跳过,不可派发」→「发修复指令不派」 | `SKILL.md` :355 (the
full rule) |
| `core-rules.md` :151 「核验对读同轮报告」→「对读同轮」 | `SKILL.md` :780 (unchanged) |

## Ceilings before / after (`node
scripts/pm/check-skill-line-ratchet.mjs`, green before and after)

| file | before | after | ceiling |
|:--|--:|--:|--:|
| `.claude/skills/pm-dispatch/SKILL.md` | 813 | 813 | 813 |
| `.claude/agents/os-dev.md` | 403 | 403 | 403 |
| `.claude/skills/pm-dispatch/references/core-rules.md` | 151 | 151 |
151 |
| `.claude/skills/pm-dispatch/references/lanes/director.md` | 72 | 72 |
72 |
| `.claude/skills/pm-dispatch/references/state-machine.md` | 42 | 42 |
42 (untouched) |

No ceiling raised, no cross-file move declared. Twin rule: a rule
changed in `SKILL.md` that has a `core-rules.md` twin changed there in
this PR (:87 for item 1, :151 for item 2); items 3, 4, 5, 8 have no
core-rules twin line, so none was added.

## Item 2's lock — measured, not written

- `.claude/settings.json` `permissions.deny` holds 17 `mcp__github__*`
write tools, including `issue_write` and `sub_issue_write` — the MCP
half of 「devs do not file」 is already mechanical (lock 1;
`check-settings-deny-roster.mjs` pins the roster).
- `permissions.allow` pre-approves `curl -sS -X POST …/issues/*/labels`
(labels) and no `POST …/issues` (create): the REST create is neither
allowed nor denied — it falls to the session's permission mode, not to a
rule.
- No hook inspects a curl target: of the five PreToolUse hooks only
`guard-main-checkout-bash.sh` mentions `curl`, as a write-redirection
shape, not a URL.
- `references/rest-channel.md` :41 declares `POST …/issues` a ✓ seat
channel (「建卡带标签 `POST .../issues`」) and the seats file cards through it;
a dev subagent runs inside the seat's session under the same
`settings.json`. A Bash deny rule on `POST …/issues` would refuse the
seat's own filing — exactly the conflict the claim anticipated — so **no
rule is added**. The report's `open_questions` carries the two
mechanisms that could bind the dev without the seat (an agent-scoped
PreToolUse hook declared in `os-dev.md` frontmatter; a guard branch
keyed on a subagent signal in the hook payload), both needing a
measurement of what the harness hands a subagent's hook, and both on the
human floor (新增必需 hook).

## Why `state-machine.md` is untouched

The claim listed it for item 4(ii). It is 42 / 42 on the ratchet
(mechanism assumption 4: the row exists), has no `needs-user-decision`
section, and its `pm:retriage` section already governs the refusal's
mechanics — :40 (挂标者 = 提出异议的席位, evidence + the ask in one stroke) and
:41 (摘标者 = 分诊 Routine, next fire). The intake duty the card itself names
(「落卡入箱时校验/补全」) lives at `SKILL.md` :357, so 4(ii) lands there. Nothing
in `state-machine.md` is a second pointer or a restated line worth
retiring to add a twin.

## Mechanism assumptions — each verified

1. **Tier.** `dispatch-gates --tier --repo objectstack-ai/objectstack`
on the five paths → MANDATORY, path-derived from `SKILL.md` and
`os-dev.md`; the register (`check-governed-merges.mjs` row
`claude-tree`) puts `.claude/**` in Tier S. `node
scripts/pm/check-governed-merges.mjs --pr N` runs after this PR opens;
its reading is in the report comment on objectstack-ai#19340 (GOVERNED, exit 3, Tier S
expected).
2. **Serial.** At 2026-09-20T21:54Z the file lists of all 21 open PRs
were read: none touches the five files; only objectstack-ai#19292 touches
`.claude/settings.json`, which this PR does not. `origin/main` moved by
one commit since the branch point (`57ceb9d`, `scripts/` only, no
`.claude/` path) — nothing to merge.
3. **Gates.** The line ratchet, the id-lint and the governed-prose pin
are all in the derived union and green (below). There is no token
ratchet over `.claude/**`: `scripts/check-skills-token-ratchet.mjs`
prices the published `skills/**` only; `.claude/**` is priced in lines
by `check-skill-line-ratchet.mjs`. Ceilings not raised.
4. **`state-machine.md` row.** Present at 42 — see above.

## Verification (head `a2665ce`)

Derived union — `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` (no paths; change set 4 paths vs merge base
`23f1de0`, 48 changed lines vs the 5000 threshold: under) → 23 commands,
each exit captured before any pipe, reconciled with `--ran`: 「23 derived
famil(ies) accounted for — 23 run, 0 NOT-MEASURED (a DERIVED zero — all
23 recorded an exit code and none of them is 3)」.

```text
pnpm check:pm-skill-ratchet :: exit 0
pnpm check:pm-skill-id-lint :: exit 0
pnpm check:pm-governed-prose :: exit 0
pnpm check:pm-governed-merges :: exit 0
pnpm check:pm-expected-skips :: exit 0
pnpm check:skill-frame-sync :: exit 0
pnpm check:agent-model-declared :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:commit-card-trailers :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
pnpm check:agent-test-spelling :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
node scripts/pm/check-governed-queue-guard.mjs --self-test :: exit 0
node scripts/pm/check-harness-current.mjs --self-test :: exit 0
pnpm check:pm-half-states :: exit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0
```

`check:doc-formula-expressions` first exited 3 (PREREQUISITE NOT MET —
`@objectstack/formula` and `@objectstack/lint` unbuilt; not a
measurement); both were built under `bash scripts/pm/os-verify-lock.sh`
(VERDICT command-exit 0, 4 tasks, cached) and the gate re-ran to exit 0.
The three named gates plus `check-nul-bytes` and
`check-agent-model-declared` were also run directly via `node` on the
working tree before the commit, exit 0 each. `pnpm
check:pm-dispatch-gates` (not in the derived union) runs detached with
its exit captured to a log; at PR open it stood at 1715 cases ✓ / 0 ✗
and still running — its final exit is in the report comment.

## 四维分析 — 每处措辞的取舍

- **实际业务需求** — 每一条都对应本场总监席实测到的成本:51 张全读后才呈第一批(第 5 项);5
张已裁卡以新速读重入决策箱、每张换来一次全线程重读与一句「维持原裁」(第 4(ii) 项);16 张裸 `finding` 积压超过运行手册
15 张告警线(第 1、2 项)。措辞只写机制与判据,不写故事;判据可核验(开场标记→首批 ≤ 读五张;线程有 `Ruling:` 而卡面缺行
⇒ 拒入)。
- **项目长远合理性** — 全部落成在位改写与退指针,不抬棘轮上限、不加文件、不加工作流;规则住在它的自然家(入箱检在 `SKILL.md`
:357 的决策箱勤务行,并卡规则在查重三行,总监节奏在章程 :40–:42),不另起一节。第 2 项的锁没有落:实测无规则拒 `POST
…/issues`,而 `settings.json` deny 会一并锁住席位自己的立卡通道 ——
加一条会破坏立卡的规则是「workaround」,故按裁决意图只落报告契约半边,机制半边作为待决问题带四轴上报。
- **防 AI 写错** — 报告契约从散文(`noted, not filed: …`)改成两个具名字段(`class: a|b|c` +
证据 / `carrier:`),缺字段的条目有确定去向(Acceptance
notes),席位处置是三选一的固定拼写;入箱拒入是机械判据(线程有 `Ruling:`/RULED 而卡面缺行),不靠席位记得去读线程 ——
这正是本场 5 张重入卡的失效形态。
- **创业阶段不扩散需求** — 未采纳的第 7、9 项与汇总卡、配额都没有被重新提出;第 1
项明确写「不进汇总卡、无配额」封住两条被拒的路。`state-machine.md` 不为一个已有归宿的规则新增一节。

## 维护者速读(草稿)

**改了什么** — 把您 2026-09-20 对立卡与并卡流程的裁定(「1 2 3 4 5 6 8」,总监席记录
5750077963)中的六项(第 6 项由 objectstack-ai#19344
另落)逐条落进章程:立卡门槛反转(只立三类,且缺陷须今天可达、元数据陷阱须具名生产者;观察类 / 休眠 / 零拉动
不立卡、不进汇总卡、不设配额);dev 不立卡,报告每条带 `class:` + 证据或 `carrier:`,席位在 ACCEPT
逐条一行处置;分诊首触定级即并同文件同机制的卡,同族异缺陷互链排同批、在飞永不并,执行席只挂 `pm:retriage`;同 assignee
的重复由持有席自关;从在飞派发升级出的卡首行写明承哪一半;线程已有裁决的卡再入决策箱必须带 `Prior rulings on this
card:` 行,否则拒入;总监席读完前五张同族卡即呈首批,一类自裁按批过,排序只用便宜扫描。四个受棘轮文件全部净 0 行(813 / 403
/ 151 / 72),每条新规则以退掉一条重复指针或一条他处已原样陈述的行付账;`state-machine.md` 未动。

**为什么改** — 章程原文与已生效裁定相反或缺失:`director.md` :40 仍写「第一步:逐卡过一类自裁」(本场总监席因此读完
51 张卡才呈第一批);`SKILL.md` 没有「首触即并」「同持有自关」「Prior rulings on this
card」任何一句;`os-dev.md` 的报告契约没有 `class:` / `carrier:` 字段,且三行仍把「立单」写成 dev
的动作。每个席位每次 fire 都读这些文件,错一句就整队照错。

**风险与代价(含回滚)** — 纯文本改动,零代码、零工作流、零 settings;派生门禁 23 / 23 绿。代价是为了净 0 而退掉的
7 行(5 条第二指针、2
条他处原样陈述的规则)与重写行里压缩掉的几个短语,每一条都在上方表格里点名了现在住在哪一行;若您认为任一条不该退,恢复它是一行
revert,但要同时退另一行才能过棘轮。第 2 项的机械锁没有落:实测今天没有任何规则拒绝 dev 会话的 REST `POST
/issues`(MCP `issue_write` 已 deny;REST 建卡既不在 allow 也不在 deny),而在
`settings.json` 加 deny 会同时锁住席位自己的立卡通道,故作为待决问题上报而不硬加。

**席位意见** —(留空,由席位定稿)

**你要做的** — 本 PR 走 Tier S 席内复核落地,不需要您点击。唯一待您一字的是第 2 项机械锁的形态(报告
`open_questions`):A 以 `os-dev.md` frontmatter 的 agent 级 PreToolUse hook
只锁 dev 会话 / B 暂不加锁、只靠报告契约与 ACCEPT 处置 / C 其它。

## Acceptance notes

- noted, not filed: `Prior rulings read:` (tool-printed by
`check-prior-rulings.mjs`, ids only; `decision-analysis.md` :45) and the
ruled `Prior rulings on this card:` (hand-written, id + one sentence
each) are now two spellings for one fact on a card face; the tool could
print a stub of the second. 承接者: the skills seat (this lane), when a
card asks for it — no PR heads for `check-prior-rulings.mjs` today.
- noted, not filed: `SKILL.md` :223 still reads 「分诊座位唯一生产 …
`duplicate_of`」 with the item-8 exception only at :370; no 120-byte
spelling of the exception fits on :223 (four variants measured 125–153
B). 承接者: the next PR that touches :223.
- noted, not filed: `os-dev.md` :48 keeps 「未演练漂移」 while the seat-side
list (`SKILL.md` :351) says 「休眠」 — same class, two words. 承接者:无.
- noted, not filed: the record's 「Tier H — the maintainer merges by
hand」 for `.claude/**` is the superseded boundary; PR objectstack-ai#19379 repairs the
two carriers still spelling it (out of scope here; objectstack-ai#19379 remains open).

---
_Generated by [Claude
Code](https://claude.ai/code/session_017ETYWqMQD4qMtZzAGovWNi)_

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…rd, decided on the committed trees (objectstack-ai#19634)

Part of objectstack-ai#19244 (the card stays open for the ruling's fourth bullet —
arm-time drift re-measurement in `references/landing-operations.md`, PR
objectstack-ai#19379's region — and for the `check-governed-merges.mjs` CURRENT-head
prose; the seat returns it to the queue at this PR's landing)

Clause-②: no

A review record binds to a head, so any push re-owes the review. On a
generated-artefact-dense surface that is a loop the reviewed seat cannot
exit: somebody else lands, baselines drift, the seat regenerates, the
head moves, the record is owed again — measured four times in one round
on this card, with two PASSed pull requests left unlanded.

The maintainer ruled it (2026-09-20, director seat batch objectstack-ai#193 item 2,
letter B′, comment 5749024878), verbatim and untranslated:

> 纯重生成提交不需要开达档复核记录

## What lands

**The rule text** — `references/contract-review.md` gains ONE line under
the head-binding sentence at :20 and retires one, so the file stays at
its ceiling of 60 (headroom 0), the new line at 110 bytes — it names the
literal `Regen-provenance:` token the reader matches, which is what the
ruling's execution paragraph orders into this line:

- 例外:纯重生成 head 后移原记录继续管;判据机读已提交树;PR 落 `Regen-provenance:` 行。

Retired as a provable duplicate: 「0 = 确定性行全清;4 = 任一不成立,只确定性行红才挡落地;3 =
环境答不了 ⛔ 不作干净。」 — its surviving homes are the checker's own header
section `## Exit codes — the refusal to read as clean, in one table`
(which AGENTS.md makes the authority on that detail, and which shows the
retired line had also drifted: 3 is PREREQUISITE NOT MET there, and 2 is
the cannot-answer verdict) and `SKILL.md` 〈入队与落地〉 :657 for 「只确定性行红才挡」.
The `Regen-provenance:` exact format now lives in the checker's C3
`moved-after-clear` remedy, the row a seat whose head moved lands on.

**The criterion, both arms, on committed trees**: of the paths `git diff
-z --name-only OLD NEW` lists, drop every one carrying `merge=os-regen`
(`git check-attr --source NEW -z merge --stdin`), then drop every one
this pull request never touched at either head — a path absent from both
`git diff --name-only MERGE-BASE-OLD OLD` and `git diff --name-only
MERGE-BASE-NEW NEW` moved only because the base moved, which is the
ruling's own 「the merge commit's own carry-over from main」. Empty is the
whole criterion. The second arm is what makes the exception fire at all:
without it a merge-forward lists every path main carried over and reads
them as hand-written.

**The mechanism**, once, in `scripts/pm/check-clause2-carriers.mjs`, and
reached by the queue guard through the lazy import it already takes (⛔
no second parser):

- `REGEN_PROVENANCE_LINE` reads one hop off either thread — the PR's or
its card's — in the shape `Regen-provenance: RECORD-ID · OLD-HEAD →
NEW-HEAD · COMMAND → (empty)`. Everything after the second sha is the
seat's own transcript and is deliberately unread.
- `regenChainToHead` walks back from the pull request's current head
over as many hops as the thread carries. Hops are de-duplicated on
record + from + to BEFORE the ambiguity test — the reader searches both
carriers and the governed text trains the dual-carrier habit, so one hop
posted on the PR and on its card is one hop — while two DIFFERENT hops
arriving at one head still end the walk rather than being ranked.
- `unexplainedPathsBetween` runs the ruled test on the two **committed**
trees: the two-dot name-only diff, the attribute read with `--source
NEW` (so `.gitattributes` itself is read out of a commit, never out of
the working tree), and the PR's own delta at each head against
`merge-base BASE head` — the base is `origin/main` in the carriers
reader and the merge group's own base sha in the queue guard.
- `regenCarry` answers four states that are never folded: `none` (no
line — today's rule, untouched), `carried`, `refused` (a line that does
not certify), `unreadable` (the environment could not answer — a commit
or the base ref this reader cannot reach).
- `gateBindingState` no longer reports `moved-after-clear` for a carried
move, and `locateReviewOfRecord` re-reads the record at the carried head
— pinned to the record id the chain names, so a line pointing at a
comment the thread does not carry certifies nothing.

**The line is a pointer, never the evidence.** Every reader re-runs the
test itself. A reader that cannot reach both commits or the base answers
with a gap: the pair is UNJUDGED in `--pair` and the queue guard refuses
on `EXIT_REFUSED_UNREADABLE` — ⛔ never clean, in either reader. A seat
that writes the line and nothing else has certified nothing.

The committed-trees half is not stylistic: it is comment 5748085403's
reading, where one un-added regeneration answered `git status`, `git
diff --cached` and `git diff` three different ways and the `--cached`
reading was main's side, which looks exactly like the answer.

## Measured, on real committed trees

Measured with the installed git (2.43.0), on this branch's OWN history
rather than a fixture:

| range | what it is | moved paths | verdict |
|---|---|---|---|
| `60c99d8` → `180ce09` | the merge-forward this PR made in round 1 | 2
hand-written `.changeset/*.md` that main brought; unexplained = 0 |
**CARRIED** (round 1's one-arm test refused exactly this commit) |
| `744a0a3` → `180ce09` | this PR's own base to its head — the lit
control, same reader, same run | unexplained = 3, this PR's own three
files | refused (correct) |
| `0b4022b` → `744a0a3` | a source change on main | 28 moved, 10 dropped
by the attribute, 18 kept | refused (correct) |

Both directions demonstrated on real trees. The line reader was also run
against the real specimen this card recorded (record 5746847791,
`5dd391125e` → `e1ae025756`) and parses it.

Ablation, one-shot through `scripts/ablation-replace.mjs` on the
committed tree: removing the hop de-duplication (blob `9203b8a3c258` →
`4369e387fbc9`, anchor 1 → 0, proved on disk) fails exactly the two pins
that cover it — 2 of 1140, by name — and the restore is byte-identical
to the HEAD blob with a clean `git diff HEAD`.

## Gates — every one, with the exit code captured before any pipe

`node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands` derived 44 families for these three paths at the final head;
all 44 ran and `--ran` reconciles 44/44 with 0 NOT-MEASURED (a derived
zero: every row recorded an exit code and none is 3).

- 44 of 44 exit 0, including `check:pm-clause2-carriers` (1140 cases;
1115 on main), `node scripts/pm/check-governed-queue-guard.mjs
--self-test` (312 cases; 301 on main), `check:pm-skill-ratchet`,
`check:pm-skill-id-lint`, `check:skill-frame-sync`,
`check:pm-dispatch-gates`, `check:pm-governed-merges`,
`check:nul-bytes`, `check:doc-authoring`,
`check:cross-package-test-inputs`.
- Also run, outside the derivation: `check:pm-governed-prose` exit 0,
`node scripts/check-skills-token-ratchet.mjs` exit 0.
- `pnpm --filter @objectstack/lint run check:doc-formula-expressions`
first exited **3** (PREREQUISITE NOT MET — nothing measured). Re-run
after `turbo run build --filter=@objectstack/formula
--filter=@objectstack/lint`: **exit 0**.
- `git merge origin/main` at the final head brought `49d5069` (17 files:
`packages/spec` field-scale and `scripts/pm/post-stamped.mjs`) — no
conflict, no `merge=os-regen` path moved, no deferral (`node
scripts/check-regen-pending.mjs` exit 0), `pnpm-lock.yaml` unmoved;
every gate above was run AFTER that merge, on the final head.

## Line ratchet — green, paid in the file's own currency

`pnpm check:pm-skill-ratchet :: exit 0` — 「contract-review.md is 60
lines (ceiling 60; headroom 0)」. The ceiling was NOT raised and
`scripts/pm/check-skill-line-ratchet.mjs` is untouched: round 1's two
lines compress to one and one provably duplicated line retires, the only
currency a line ratchet takes. Two other duplicate candidates were
examined and left in place because each carries a residue with no
surviving home (a scheduling clause at :26; the ③ of a numbered list at
:47).

## Concurrency

PR objectstack-ai#19379 (draft, `claude/pm-superseded-references-tier`) is open on
`scripts/pm/check-governed-queue-guard.mjs`. Its diff was read first: it
rewrites the THIRD-leg header prose around the superseded
references-tier wording (lines ~279–310) and one self-test summary
string. This PR touches neither — its hunks are `git()`'s stdin,
`runGuard`'s reader parameter, the pair it builds, `recordVerdict`'s
carried fields, the CLEAR rendering and a new self-test battery. The
later lander merges once. `origin/main` was merged into this branch
before this PR opened.

`objectstack-ai#19068` — the `exports`-map sentence in the same reference file — is a
different card and a different region; it is not addressed here and
remains open.

## Acceptance notes

- `check-half-states.mjs` H51 was measured, as the ruling's execution
stroke asks: it does **not** refuse a moved head. It fires only on a
verdict for the **current** head while the label is still hung, and says
so itself — "A review naming an OLDER head is NOT this row". It is
report-only patrol input and writes nothing, so nothing there needed
changing.
- `check-governed-merges.mjs` is the post-merge audit and refuses
nothing; its Tier S prose says a record "for the CURRENT head". That
sentence is now narrower than the rule, but it is prose in a report-only
tool and outside this card's file surface.
- The ruling's fourth bullet — arm-time re-measurement of drift becoming
standing practice — is about landing operations, not carrier discipline,
and `references/landing-operations.md` is PR objectstack-ai#19379's region. It is not
landed here.
- The queue battery's former duplicate specimen is now a real class-(ii)
case — a generated path moved BESIDE one of this pull request's own. The
queue renders a refused carry as an absent record and does not print the
path, so the battery asserts the name at `unexplainedPathsBetween`, the
reader that owns the reason. Making the queue print it is a ~9-line
change, named and not taken.
- The de-duplication key is the exact record + from + to triple: the
same hop spelled with DIFFERENT sha abbreviations on the two carriers is
still two hops and still ends the walk — the refusing direction.
- `skip-changeset`: `.claude/**` and `scripts/pm/**` are in no package's
`files[]`; this diff publishes nothing.


---
_Generated by [Claude Code](https://claude.ai/code)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…s; H43/H48 stand down on Tier S (objectstack-ai#19802)

Part of objectstack-ai#19146

Clause-②: no

## 维护者速读(草稿)

**改了什么**:两处。① `.claude/agents/os-dev.md` :286–:287 两行(净 0
行,402/402)从已废止的「事实层 = `references/` 目录、余为规则层」改写成登记表现行的两层:受管路径全在
`.claude/**` 者 Tier S,达档复核 PASS 在案即由席位入队落地;余皆 Tier H 等人批;`## 维护者速读(草稿)`
两层同欠(第二行不再豁免「事实层」)。② `scripts/pm/check-half-states.mjs` 的 H43(受管 PR
未向授权账户请审)与 H48(ACCEPT 后缺 `needs-user-decision` 标签或速读评论)在 Tier S PR
上停手:懒加载的登记表多带出 `landingTierOf` 与 `GOVERNED_TIER_S`,一个
`governedLandsOnRecord` 助手在两行已匹配的切片上问登记表,S 则不判;混合 diff 有一条 Tier H
命中仍判;登记表没加载则一律不停手。自测新增 15 例(4897 → 4912),用真实登记表自己的切片驱动;⛔ 不新增巡查行、不新增电池。

**为什么改**:这两行是每个 dev 子代理都读的自我定义,写的是 2026-09-18 分层裁决之前的边界;H43/H48 在 Tier S
PR 上开的处方(请授权账户审、挂维护者收件箱标签)正是裁决取消的那一下点击,且 H43 的理由句「队列守卫仍拒收未批准的受管入队」对 Tier
S 为假(PR objectstack-ai#19351 零批准、凭复核记录经队列落地)。

**风险与代价(含回滚)**:文字面零行为变化。H43/H48 在 Tier S PR 上不再报告——这是裁决的意图,不是丢失:Tier S
的落地由队列守卫按 `## Contract review` 记录把关。Tier H 与混合 diff 的行为字节不变;登记表缺席时仍照旧报
NOT MEASURED。消融证明:把 tier 读取删掉,5/4912 例转红,还原后 blob == HEAD。回滚 = revert
两个提交(各一类,可单独回退)。

**席位意见**:(席位填写)

**你要做的**:本 PR 为 Tier S(`.claude/**`),由归属席位在 `## Contract review` PASS 记录
+ 全绿后经队列落地,⛔ 不需要您点击。卡上余项一条(`SKILL.md:618`,见下方 On the card),由 skills
席位自处置。

## Summary

The two `os-dev.md` lines still spelled the pre-tiering split (fact
layer = the `references/` directory, everything else rules layer). They
are re-keyed to the register's rule as `AGENTS.md` Prime Directive objectstack-ai#14
states it: a PR whose governed paths ALL lie under `.claude/**` is Tier
S and lands on the owning seat's `## Contract review` record; every
other governed surface is Tier H and waits for the maintainer's word.
The maintainer-brief draft is owed on both tiers (SKILL.md :623 has no
tier split; the seat fills 席位意见 on both), so the second line simply
stops exempting a "fact layer".

The card also names `check-half-states.mjs` H48 and H43. Measured on the
base: both rows still exist and are tier-blind (`governedTierFor` /
`landingTierOf`: 0 hits in the file; H43 reads `GOVERNED_APPROVERS`
only). On a Tier S PR each remedy asks for the click the tiering
removed, so both stand down there. The tier is the register's own answer
on the slice the matcher already returned — no second list of surfaces
lives in the patrol.

## Per-site before → after

### `.claude/agents/os-dev.md` (402 → 402 lines, net 0; both lines
within the 120-byte cap)

| line | before | after | bytes |
|---|---|---|---|
| :286 | 「- 受管路径全在 `.claude/skills/pm-dispatch/references/`
者为事实层,席位复审即记录;余为规则层。」 | 「- 受管路径全在 `.claude/**` 者 Tier S,达档复核 PASS
在案即由席位入队落地;余皆 Tier H 等人批。」 | 118 → 120 |
| :287 | 「- 规则层 PR 正文带 `## 维护者速读(草稿)` 节,中文、业务角度,席位意见留空;事实层不欠。」 | 「- 受管面
PR 正文带 `## 维护者速读(草稿)` 节,中文、业务角度,席位意见留空;两层同欠。」 | 117 → 114 |

The rule they now mirror: `AGENTS.md` :272–:280 (Tier H = `docs/adr/**`,
`docs/NORTH-STAR.md`, `skills/**`, `AGENTS.md`, `CLAUDE.md`, an
authorized APPROVED review; Tier S = all of `.claude/**`, a `## Contract
review` record for the current head with `Served-tier:
CONTRACT_REVIEW_TIER` and a PASS verdict, the owning seat lands it
through the queue) and SKILL.md :623 (「草稿归 dev:受管面 PR 正文带 `##
维护者速读(草稿)`」, no tier split). `grep -n 事实层 .claude/agents/os-dev.md` on
the head: 0 hits.

### `scripts/pm/check-half-states.mjs` (H43 / H48)

| site | before | after |
|---|---|---|
| `loadGovernedRegister` | reads `governedPathsIn` +
`GOVERNED_APPROVERS` | also reads `landingTierOf` + `GOVERNED_TIER_S`
(`tierOf`, `recordTier`); a register missing any of the four is "did not
export what this row reads", as before |
| new `governedLandsOnRecord(governed, register)` | — | `true` only when
the register is available AND `landingTierOf(slice) ===
GOVERNED_TIER_S`; `false` for a Tier H or mixed slice, an empty or
tier-less slice, or an unloaded register |
| `h43NeedsReviewProbe(pr, governedCount, approvers)` | took a COUNT |
takes the matched SLICE, stands down on Tier S — a Tier S PR buys no
review page and takes no slot under the oldest-first cap |
| `h43GovernedReviewRequestGap` | fired on every governed PR short of
coverage | `null` on a Tier S slice; the sentence reads "open and
GOVERNED on Tier H" and "refuses an unapproved Tier H enqueue" |
| `h48SpeaksAbout` | population = governed ∧ open | ∧ not Tier S — so
`h48GovernedVerdictWithoutBrief` is `null` there and the sweep buys no
PR comment thread for it |
| H43 / H48 headers, both summary clauses | tier-blind prose | name Tier
H as the population; Tier S stated as out |
| sweep call site | `h43NeedsReviewProbe(pr, governedByPr.get(n)?.length
?? 0, …)` | `h43NeedsReviewProbe(pr, governedByPr.get(n) ?? [], …)` |

Self-test: 15 new `t()` cases (no new battery, no floor moved): the Tier
S and mixed slices come from `GOVERNED_REGISTER.matcher(...)` on the
real register, so the tier answer is the register's; the hand-built
`GOV43` / `GOV48` fixtures carry no `tier` and read as H (fail closed),
which the comments now say. Register pin extended:
`tierOf(matcher(['.claude/agents/os-dev.md'])) === recordTier`. Case
counts: 4897 on the base → 4912.

Why stand down rather than re-aim: the card offered both. Re-aiming H43
at "a Tier S PR without a review of record" would be a new patrol row in
disguise (新增门禁默认否), and the queue guard already refuses a Tier S enqueue
without the record — nothing ships through that gap. Standing down is
the minimal, mechanical repair the card's item 3 spells for H48 and it
is the same change class for H43, so the two ride together.

## Measurement the change rests on (reads taken 2026-09-23T05:24Z–05:36Z
against `origin/main` = `2cf9db7c4`; each item names its own clock)

- 2026-09-23T05:24Z — `.claude/agents/os-dev.md` :286–:287 read
byte-identical to the card's quotation (premise valid).
- 2026-09-23T05:25Z — `check-half-states.mjs` @ `2cf9db7c4`: H48 row at
:10888–:11100, H43 row at :9607–:9850; `governedTierFor` /
`landingTierOf` / `recordTier`: 0 hits in the 34,841-line file;
`GOVERNED_APPROVERS` is H43's only firing control. PR objectstack-ai#19737 retired
H31/H35/H51/H53/H61 and left both rows in place.
- 2026-09-23T05:30Z — the seat's practice on a landed Tier S PR (objectstack-ai#19351,
all `.claude/**` + `scripts/pm/`): 0 reviews, no review request, no
`**ACCEPT**` on the thread, no `needs-user-decision`, no `## 维护者速读`
comment — one `## Contract review` PASS record, landed through the
queue. H43's shape fires on exactly that PR; it fired on objectstack-ai#19379 too
(card comment 5750573385).
- 2026-09-23T05:36Z — register verdict on this PR's two paths:
`check-governed-merges.mjs --test` → `GOVERNED — Tier S(席内达档复核落地)`, exit
3 (= EXIT_TEST_GOVERNED); `scripts/pm/check-half-states.mjs` is not on
the register.
- 2026-09-23T05:25Z — no open PR touches either file (all 16 open PRs'
file lists read over REST at claim time).

## Reverse verification (ablation), run 2026-09-23T05:37Z at head
`bd5bbd2bb` (the file is byte-identical at `c9617adde`)

`node scripts/ablation-replace.mjs --file
scripts/pm/check-half-states.mjs --anchor ' return register.tierOf(list)
=== register.recordTier;' --replacement ' return false; // ABLATION:
tier reading removed' -- node scripts/pm/check-half-states.mjs
--self-test`

- mutation landed on disk: anchor 1 → 0, marker 0 → 1, blob
`d2d9ba38ac63` → `52c8e2e35f0d`
- direction observed: RED — `✗ check-half-states self-test: 5 of 4912
case(s) failed` (the H43 Tier S clean case, the H43 Tier S probe case,
the register-answer join case, the H48 Tier S population case, the H48
Tier S no-finding case); the mixed-slice controls stayed green
- restore proven: blob after restore `d2d9ba38ac63` == blob at HEAD;
`git diff HEAD` empty; re-read on the absolute path: anchor 1, marker 0
- no `dist/` is involved (the patrol runs from source), so no build leg

## Gates on the final head `c9617adde` (run 2026-09-23T06:02Z–06:19Z;
exit codes captured before any pipe; verdict lines from the gate logs)

Derived with `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` on this head (47 commands, list
identical to the derivation at `bd5bbd2bb`); `--ran` reconciliation: `✓
dispatch-gates --ran: 47 derived famil(ies) accounted for — 46 run, 1
NOT-MEASURED (1 DERIVED from a recorded exit 3).`.

| # | command | exit | verdict line (from the gate log) |
|---|---|---|---|
| 01 | `node scripts/check-ci-filter-parity.mjs` | 0 | OK: all 185
declared cross-package glob(s) (132 unique) are covered by `core` or
`crosspkg`, every `crosspkg` entry still covers one, and the `test` j |
| 02 | `node scripts/check-closing-keyword-parity.mjs` | 0 | •
packages/spec/CHANGELOG.md -- 6080503 bytes exceeds the sweep's
2097152-byte cutoff for UNREGISTERED files |
| 03 | `node scripts/check-closing-keyword-parity.mjs --self-test` | 0 |
✓ check-closing-keyword-parity --self-test: 40 assertions, 5 mutations
of the shipped parsers each driven to red. |
| 04 | `node scripts/check-comment-mask-corpus.mjs` | 0 | ✓ comment-mask
corpus sweep [scripts/js-comment-mask.mjs]: 7015 files, 0 disagree, 0
unparseable, 60.0s (comparator self-test: 26 cases pass). |
| 05 | `node scripts/check-declaration-mirrors.mjs` | 0 |
scripts/invoked-as.d.mts |
| 06 | `node scripts/check-declaration-mirrors.mjs --self-test` | 0 |
All 29 self-test cases passed. |
| 07 | `node scripts/check-scripts-symbol-anchors.mjs` | 0 | ✅
check-scripts-symbol-anchors: 3684 anchors across 280 scripts resolve —
52 symbol (52 declaration, 0 literal), 3632 file-level, 0 cross-repo, 1
exem |
| 08 | `node scripts/check-scripts-symbol-anchors.mjs --self-test` | 0 |
✅ check-scripts-symbol-anchors --self-test: every finding class
provoked, comment-prose projection wired, declined shapes counted not
missed, allowanc |
| 09 | `node scripts/check-self-test-wired.mjs` | 0 | ✓
check-self-test-wired: every one of the 229 script(s) CI runs that ship
a `--self-test` has that self-test run by CI. |
| 10 | `node scripts/check-self-test-wired.mjs --self-test` | 0 |
check-self-test-wired --self-test: 3 live ledger row(s) verified, plus
the comment mask, the right boundary, alias resolution and both audit
direction |
| 11 | `node scripts/check-self-test-workflow-commands.mjs` | 0 | scope:
229 script(s) CI runs ship a `--self-test` (0 of them package-local
gate(s) CI names by path, present because this population is the one
chec |
| 12 | `node scripts/check-self-test-workflow-commands.mjs --self-test`
| 0 | check-self-test-workflow-commands --self-test: both measured parse
rules pinned (legacy form anywhere in a line, current form only at line
start), the |
| 13 | `node scripts/check-whole-set-label-write.mjs` | 0 | PROSE_PROBES
make `run()` refuse rather than pass if it ever stops finding them. |
| 14 | `node scripts/check-whole-set-label-write.mjs --self-test` | 0 |
✓ check-whole-set-label-write --self-test: all cases pass (24 fixture
trees + 5 refusals + 1 allowlist hatch) |
| 15 | `node scripts/pm/bare-root-worklist.mjs --self-test` | 0 | OK
self-test: 81 live row(s), 59 unreachable as spelled, 46 recorded
verdict(s) — none stale, none missing, none contradicted (12 row(s)
whose gate c |
| 16 | `node scripts/pm/board-snapshot.mjs --self-test` | 0 | OK
board-snapshot self-test: 156 cases pass across 12 batteries (open-first
walk order, the delta-first run order and its budget split driven end to
e |
| 17 | `node scripts/pm/check-governed-queue-guard.mjs --self-test` | 0
| ✓ check-governed-queue-guard self-test: 279 cases pass
(register-driven verdicts, the queue/PR event split, latest-decisive
approval reduction, the 20 |
| 18 | `node scripts/pm/check-harness-current.mjs --self-test` | 0 |
check-harness-current --self-test: all 26 cases passed. |
| 19 | `node scripts/pm/sweep-closed-cards.mjs --self-test` | 0 | ✓
sweep-closed-cards self-test: 87 cases pass across 9 batteries (the
imported residue set, the offline screen, the two closing routes with
the measur |
| 20 | `pnpm --filter @objectstack/lint run
check:doc-formula-expressions` | 3 | Exit status 3 |
| 21 | `pnpm check:agent-model-declared` | 0 | ✓
check-agent-model-declared: 1 agent definition(s) under .claude/agents/
all declare a model |
| 22 | `pnpm check:agent-test-spelling` | 0 | ✓
check-agent-test-spelling: 0 violations — 560 file(s) · 9601 bare `--`
token(s) · 1810 launcher-rooted run(s) · 13 separator(s) JUDGED · 6
vitest-ba |
| 23 | `pnpm check:bash32-floor` | 0 | ✓ check-bash32-floor: 33 tracked
shell file(s) under scripts/**, .claude/hooks/**, .githooks/** name no
bash 4+ construct outside a comment, a guarded |
| 24 | `pnpm check:cli-command-ids` | 0 | ✓ check-cli-command-ids: 63
module(s) under packages/cli/src/commands examined, all of them
default-export a class whose inheritance chain reaches ocl |
| 25 | `pnpm check:closing-target-claim` | 0 | ✓
check-closing-target-claim self-test: 105 cases pass. |
| 26 | `pnpm check:commit-card-trailers` | 0 | ✓
check-commit-card-trailers self-test: 81 cases pass. |
| 27 | `pnpm check:cross-package-test-inputs` | 0 | All 255 self-test
cases passed. |
| 28 | `pnpm check:doc-authoring` | 0 | ✓ doc authoring guard:
sibling-package prose ids hold the baseline — 819 pinned site(s) across
231 file(s), 90595 string(s) read in 1247 parsed source |
| 29 | `pnpm check:driver-memory-census` | 0 |
check-driver-memory-census: OK — every declaration is ledgered, every
ledger entry is live, and every ruled file states "objectstack-ai#6664 census: 2 ruled
consume |
| 30 | `pnpm check:entry-guard` | 0 | ✓ check:entry-guard: 280 scripts/
file(s) — every entry guard goes through invoked-as.mjs; 219 export
bindings, 219 of them inert on import (0 known-u |
| 31 | `pnpm check:gitlink-declared` | 0 | ✓ check-gitlink-declared
--self-test: 36 assertions over throwaway git repos (real scan() path) |
| 32 | `pnpm check:issue-citations` | 0 | ✅ check-issue-citations
--self-test: grammar narrowed, four 404 causes kept apart, both board
strategies agree, diff scope red AND green, scope contra |
| 33 | `pnpm check:nul-bytes` | 0 | ✓ check-nul-bytes --self-test: 75
assertions over a temp git repo (real scan() path) |
| 34 | `pnpm check:parse-guard` | 0 |
packages/cli/test/published-subpath-hook-body.pin.test.ts:417
ts.createSourceFile |
| 35 | `pnpm check:partof-closing-keyword` | 0 | ✓
check-partof-closing-keyword self-test: 45 cases pass. |
| 36 | `pnpm check:pm-governed-merges` | 0 | ✓ check-governed-merges
--self-test: 441 assertions (the unified governed predicate + near
misses, subject→PR spellings, window parsing, the objectstack-ai#12633 la |
| 37 | `pnpm check:pm-half-states` | 0 | ✓ check-half-states self-test:
4912 cases pass. Batteries: H66 released queue card 182/172, H19
judged-set founding 37/34, H65 tier declaration spelli |
| 38 | `pnpm check:pm-post-stamped` | 0 | ✓ post-stamped self-test: 610
cases pass across 21 batteries — offline, no network, no token. |
| 39 | `pnpm check:pm-skill-id-lint` | 0 | ✓ check-skill-id-lint: 30
file(s) clean (pattern /#[0-9]{3,}/g). |
| 40 | `pnpm check:pm-skill-ratchet` | 0 | ✓ check-skill-line-ratchet:
declared cross-file moves: 1, total ceilings down 9 lines. |
| 41 | `pnpm check:pnpm-filter-targets` | 0 | ✓
check:pnpm-filter-targets: 152/207 `--filter` occurrence(s) across 41
file(s) resolve against 81 workspace package(s); 55 not judged (2
foreign, 30 |
| 42 | `pnpm check:ratchet-remedy-authority` | 0 | OK self-test: the
lexer holds, messages are bounded, both offer word orders and path-named
registries are reached, declaration registries are not, th |
| 43 | `pnpm check:refd-timer-probe` | 0 | ✓ check-refd-timer-probe
self-test: 11 cases pass, negative controls included. |
| 44 | `pnpm check:single-claim-paths` | 0 | ✓ check-single-claim-paths
self-test: 93 cases pass. |
| 45 | `pnpm check:skill-frame-sync` | 0 | ✓ check-skill-frame-sync: the
one declared copy of the decision frame is internally coherent
(.claude/skills/pm-dispatch/SKILL.md; no second copy to c |
| 46 | `pnpm check:watch-hint-literal` | 0 | ✓ check-watch-hint-literal:
71 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 47,
ROOT_FILE_WATCH_HINTS 13, ROOT_WATCH_HINTS 3, DECL |
| 47 | `pnpm check:pm-dispatch-gates` | 0 | ✓ dispatch-gates self-test:
1905 cases pass. (738.5s on this box) |

Row 20 is exit 3 = PREREQUISITE NOT MET (the gate's own NOT-MEASURED
code, see Acceptance notes); every other row exit 0. The same 47 ran at
`bd5bbd2bb` with the same readings before the merge of `origin/main`.

## Line budget

- `.claude/agents/os-dev.md`: 402 / ceiling 402 before and after
(headroom 0, net 0 lines); `check:pm-skill-ratchet` exit 0 on the head.
Max content bytes per line 120 before and after.
- `skills/**` (the published catalog) is not touched, so no whole-file /
whole-package token readings are owed.
- `.claude/**` and `scripts/pm/**` publish nothing from any package's
`files[]` (fast lane), so `skip-changeset` applies and no changeset is
written.

## On the card

- `landing-operations.md` :27–:28 and the queue guard's SUCCESS line:
landed by PR objectstack-ai#19379 (merged `1f53b0b685`); the guard's :236 hit is a
verbatim ruling quotation and stays.
- `os-dev.md` :286–:287, H48, H43: this PR.
- REMAINDER, measured on `origin/main` at `fae870352` and not in this
PR's claimed file surface: `.claude/skills/pm-dispatch/SKILL.md:618`
(the size clause ending 「⛔ 无事实层例外」) still spells the retired word (the
skills seat's addendum 5737973707 joined it to this card), and 「规则层」
survives as a synonym for Tier H at `SKILL.md:617`,
`references/core-rules.md:122`, `references/landing-operations.md:26`
and `references/lanes/skills.md:17` (vocabulary only; each rule stays
true). That is why the first line is `Part of` rather than a closing
keyword: objectstack-ai#19146 remains open after this PR merges, with its own addendum
item still owed. The dispatch asked for a closing first line on the
premise that the two `os-dev.md` lines were the whole remainder; the
tree-wide grep says otherwise, and os-dev.md's rule (a PR whose merge
should not close the card uses `Part of`) wins.

## Acceptance notes (observations, not filed)

- `check-half-states.mjs` H48's sentence still says the handoff exists
because "a Tier H surface lands only on the maintainer's word" —
accurate for Tier H under PD objectstack-ai#14 (the maintainer's hand or an authorized
approval); the pre-existing "by hand" wording was narrowed to that in
the same edit.
- `pnpm --filter @objectstack/lint run check:doc-formula-expressions`
answers exit 3 PREREQUISITE NOT MET in this worktree (the gate needs
`@objectstack/formula` / `@objectstack/lint` built; this diff touches no
package, so no build closure is owed). Recorded as NOT MEASURED, not as
a failure; its population (docs formula expressions) is disjoint from
both changed paths.
- Landing: Tier S — draft stays draft; the owning seat renders the `##
Contract review` record for head `c9617adde` and lands it through the
queue after every check is green.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants